/* $Id: stack.c 13 2006-12-14 21:07:45Z judas $ */

#include <nih/cdefs.h>

#include <nih/list.h>
#include <nih/stack.h>

#include <errno.h>
#include <stdlib.h>

static int check_arg(const struct nih_stack *stack, int dir);

struct nih_stack *nih_screate_len(ssize_t maxlen)
{
        struct nih_stack *stack;

        stack = calloc(1, sizeof(struct nih_stack));

        if (stack) {
                /* Empty linked list */
                stack->list = NULL;
                stack->maxlen = maxlen;
                stack->len = 0;
        }

        return stack;
}

inline int check_arg(const struct nih_stack *stack, int dir)
{
        int res;

        res = -1;

        if (!stack)
                errno = EINVAL;
        else if (dir > 0 && stack->maxlen >= 0
                 && stack->len == stack->maxlen) {
                /* Pushing to full. */
                errno = ERANGE;

        } else if (dir < 0 && stack->len == 0) {
                /* Popping from empty. */
                errno = ERANGE;

        } else
                res = 0;

        return res;
}

int nih_spush(struct nih_stack *stack, const void *value)
{
        int res;

        res = -1;

        if (check_arg(stack, 1) == 0) {
                struct nih_llist *nhead;

                nhead = nih_lcreate(stack->list, value);
                if (nhead) {
                        stack->list = nhead;
                        stack->len++;
                        res = 0;
                }
        }

        return res;
}

int nih_spop(struct nih_stack *stack, void **value)
{
        int res;

        res = -1;

        if (!value)
                errno = EINVAL;
        else if (check_arg(stack, -1) == 0) {
                struct nih_llist *nhead;

                *value = stack->list->value;
                nhead = stack->list->next;

                /* Remove first element from list. */
                stack->list->next = NULL;
                (void) nih_lremove(stack->list);

                stack->list = nhead;
                stack->len--;
                res = 0;
        }

        return res;
}

int nih_sdestroy_deep(struct nih_stack *stack, nih_free_fnc *free_fnc)
{
        int res;

        res = -1;

        if (!stack)
                errno = EINVAL;
        else {
                (void) nih_ldestroy_deep(stack->list, free_fnc);
                free(stack);
                res = 0;
        }

        return res;
}

